#include "StdAfx.h"
#include "POP3.h"
#include "tool.h"
#include "TcpClient.h"

#include <algorithm>
 
namespace libdmail
{
	POP3::POP3(void): mTcpClient(NULL), mPOP3Server(""), mPOP3Port(""), mPOP3Status(POP3WELCOME)
	{
		this->mTcpClient = new TcpClient(this);
	}

	POP3::~POP3(void)
	{
		delete this->mTcpClient;
		this->mTcpClient = NULL;
	}

	int POP3::connectPOP3Server(string pop3Server, string pop3Port, string user, string pass)
	{
		this->mPOP3Server = pop3Server;
		this->mPOP3Port = pop3Port;

		this->mTcpClient->connectServer(this->mPOP3Server, this->mPOP3Port);

		this->mPOP3User = user;
		this->mPOP3Pass = pass;

		this->mTcpClient->receiveData();
		
		return 0;
	}

	int POP3::onDataReceived(stringstream *str, int strLen)
	{
		int iRet = this->response(str, strLen);

		return iRet;
	}

	int POP3::sendData(string data)
	{
		return this->mTcpClient->sendData(data);
	}

	int POP3::requestUSER()
	{
		string user = "user " + this->mPOP3User + "\r\n";
		return this->sendData(user);
	}

	int POP3::requestPASS()
	{
		string pass = "pass " + this->mPOP3Pass + "\r\n";
		return this->sendData(pass);
	}

	int POP3::requestSTAT()
	{
		return this->sendData("stat\r\n");
	}

	int POP3::requestLIST()
	{
		return this->sendData("list\r\n");
	}

	int POP3::requestUIDL()
	{
		return this->sendData("uidl\r\n");
	}

	int POP3::requestRETR(int num)
	{
		string strRETR = "RETR ";
		char numBuff[10];
		strRETR += itoa(num,numBuff,10); 
		strRETR += "\r\n";

		return this->sendData(strRETR.c_str());
	}

	int POP3::requestQUIT()
	{
		return this->sendData("quit\r\n");
	}

	int POP3::response(stringstream *strStrm, int strLen)
	{
		int iRet = -1;

		switch(this->mPOP3Status)
		{
		case POP3WELCOME:
			iRet = this->responseGREET(strStrm, strLen);
			if (0 ==  iRet) 
			{
				this->requestUSER();
				this->mPOP3Status = POP3USER;
			}
			break;

		case POP3USER:
			iRet = this->responseUSER(strStrm, strLen);
			if (0 == iRet)
			{
				this->requestPASS();
				this->mPOP3Status = POP3PASS;
			}
			break;

		case POP3PASS:
			iRet = this->responsePASS(strStrm, strLen);
			if (0 == iRet)
			{
				this->requestSTAT();
				this->mPOP3Status = STAT;
			}
			
			if (-1 == iRet)
			{

			}

			break;

		case STAT:
			iRet = this->responseSTAT(strStrm, strLen);
			if (0 == iRet)
			{
				this->requestLIST();
				this->mPOP3Status = LIST;
			}
			break;

		case LIST:
			iRet = this->responseLIST(strStrm, strLen);
			if (0 == iRet)
			{
				this->requestUIDL();
				this->mPOP3Status = UIDL;
			}
 
			if (1 == iRet)
			{
			}

			if (-1 == iRet)
			{

			}
			break;

		case UIDL:
			iRet = this->responseUIDL(strStrm, strLen);
			if (0 == iRet)
			{
				// Temporary set mMailTnfoToGetStack with mMailInfoVector
				for(int i=0; i<this->mMailInfoVector.size(); i++)
				{
					this->mMailTnfoToGetStack.push(this->mMailInfoVector[i]);
				}

				//mMailInfoCurrentIndex = this->mMailInfoVector.size() - 1;
				mMailInfoCurrentIndex = 21;

				if (!this->mMailTnfoToGetStack.empty())
				{
					this->myPOP3Mbox.open("Inbox.mbox", ios::out | ios::app | ios::binary);

					//this->requestRETR(this->mMailTnfoToGetStack.top().mNum);
					this->mMailInfoCurrentSize = 0;
					this->requestRETR(this->mMailInfoVector[this->mMailInfoCurrentIndex].mNum);
					this->mMailTnfoToGetStack.pop();
					this->mPOP3Status = RETR;
				}
				else
				{
					this->requestQUIT();
					this->mPOP3Status = POP3QUIT;
				}
			}

			if (2 == iRet)
			{
			}
			
			if (1 == iRet)
			{
			}
			break;
		case RETR:
			iRet = this->responseRETR(strStrm, strLen);

			if (0 == iRet)
			{
				if (!this->mMailTnfoToGetStack.empty())
				{
					//this->requestRETR(this->mMailTnfoToGetStack.top().mNum);
					this->mMailInfoCurrentSize = 0;
					this->mMailInfoCurrentIndex--;
					this->requestRETR(this->mMailInfoVector[this->mMailInfoCurrentIndex].mNum);
					this->mMailTnfoToGetStack.pop();
					this->mPOP3Status = RETR;
				}
				else
				{
					this->requestQUIT();
					this->mPOP3Status = POP3QUIT;
				}
			}

			break;
		case POP3QUIT:
			iRet = this->responseQUIT(strStrm, strLen);

			if (0 == iRet)
			{
				this->mPOP3Status = END;
			}
			break;

		case END:
			
			iRet = 0;

			break;

		default:
			break;
		}

		return iRet;
	}

	int POP3::responseGREET(stringstream *strStrm, int strLen)
	{
		bool bNeedMoreStr = false;
		string strLine = tool::readStreamLine(strStrm, strLen, false, &bNeedMoreStr);

		if (!bNeedMoreStr)
		{
			if(string::npos != strLine.find("+OK"))
			{
				return 0;
			}
		}

		return 1;

	}

	int POP3::responseUSER(stringstream *strStrm, int strLen)
	{
		bool bNeedMoreStr = false;
		string strLine = tool::readStreamLine(strStrm, strLen, false, &bNeedMoreStr);

		if (!bNeedMoreStr)
		{
			if(string::npos != strLine.find("+OK"))
			{
				return 0;
			}
		}

		return 1;
	}

	int POP3::responsePASS(stringstream *strStrm, int strLen)
	{
		bool bNeedMoreStr = false;
		string strLine = tool::readStreamLine(strStrm, strLen, false, &bNeedMoreStr);

		if (!bNeedMoreStr)
		{
			if(string::npos != strLine.find("+OK"))
			{
				return 0;
			}
			
			if(string::npos != strLine.find("-ERR"))
			{
				return -1;
			}
		}
		return 1;
	}

	int POP3::responseSTAT(stringstream *strStrm, int strLen)
	{
		bool bNeedMoreStr = false;
		string strLine = tool::readStreamLine(strStrm, strLen, false, &bNeedMoreStr);

		if (!bNeedMoreStr)
		{
			return 0;
		}

		return 1;
	}
	
	int POP3::responseLIST(stringstream *strStrm, int strLen)
	{
		bool bNeedMoreStr = false;
		string strLine = tool::readStreamLine(strStrm, strLen, false, &bNeedMoreStr);

		int iTotalLineLen = strLine.length();
		if (!bNeedMoreStr)
		{
			// Process strLine
			if((string::npos == strLine.find("+OK")) && (string::npos == strLine.find("-ERR")) && (string::npos == strLine.find(".")))
			{
				MailInfo mailInfo;
				stringstream ss(strLine); 
	 
				ss >> mailInfo.mNum;
				ss >> mailInfo.mSize;
				
				this->mMailInfoVector.push_back(mailInfo);
			}

			while(strLen != iTotalLineLen)
			{
				strLine = tool::readStreamLine(strStrm, strLen - iTotalLineLen, true, &bNeedMoreStr);
				
				if (!bNeedMoreStr)
				{
					// Process strLine
					if((string::npos == strLine.find("+OK")) && (string::npos == strLine.find("-ERR")) && (string::npos == strLine.find(".\r\n")))
					{
						MailInfo mailInfo;
						stringstream ss(strLine); 
			 
						ss >> mailInfo.mNum;
						ss >> mailInfo.mSize;

						this->mMailInfoVector.push_back(mailInfo);
					}
				}

				iTotalLineLen += strLine.length();

				if(string::npos != strLine.find("-ERR"))
				{
					return -1;
				}
			}

			if (".\r\n" == strLine)
				return 0;
			else
				return 1;
		}
		else
		{
			return 1;
		}

		return 1;
		
	}

	int POP3::responseUIDL(stringstream *strStrm, int strLen)
	{
		bool bNeedMoreStr = false;
		string strLine = tool::readStreamLine(strStrm, strLen, false, &bNeedMoreStr);

		int iTotalLineLen = strLine.length();
		if (!bNeedMoreStr)
		{
			if((string::npos == strLine.find("+OK")) && (string::npos == strLine.find("-ERR")) && (string::npos == strLine.find(".\r\n")))
			{
				int  iVector;
				stringstream ss(strLine); 
	 
				ss >> iVector;
				ss >> this->mMailInfoVector[iVector - 1].mUidl;
			}

			while(strLen != iTotalLineLen)
			{
				strLine = tool::readStreamLine(strStrm, strLen-iTotalLineLen, true, &bNeedMoreStr);
			
				if (!bNeedMoreStr)
				{
					if((string::npos == strLine.find("+OK")) && (string::npos == strLine.find("-ERR")) && (string::npos == strLine.find(".\r\n")))
					{
						int  iVector;
						stringstream ss(strLine); 
			 
						ss >> iVector;
						ss >> this->mMailInfoVector[iVector - 1].mUidl;
					}
				}

				iTotalLineLen += strLine.length();

				if (iTotalLineLen > strLen)
				{
					int i = 0;
				}

				if(string::npos != strLine.find("-ERR"))
				{
					return -1;
				}
			}

			if (".\r\n" == strLine)
				return 0;
			else
				return 1;
		}
		else
		{
			return 1;
		}

		return 1;
	}

	int POP3::responseRETR(stringstream *strStrm, int strLen)
	{
		bool bNeedMoreStr = false;
		string strLine = tool::readStreamLine(strStrm, strLen, false, &bNeedMoreStr);

		int iTotalLineLen = 0;

        do
        {
            if (!bNeedMoreStr)
		    {
                iTotalLineLen += strLine.length();

                if (".\r\n" == strLine)
                {
					if (this->mMailInfoCurrentSize < this->mMailInfoVector[this->mMailInfoCurrentIndex].mSize)
					{
						this->myPOP3Mbox.write(strLine.c_str(), strLine.length());
						this->mMailInfoCurrentSize += strLine.length();
					}
					else
					{
						return 0;
					}
                }
                else if(string::npos != strLine.find("-ERR  "))
				{
					return -1;
				}
                else if(string::npos != strLine.find("+OK "))
			    {
    			 
			    }
                else
                {
					this->myPOP3Mbox.write(strLine.c_str(), strLine.length());
 
					this->mMailInfoCurrentSize += strLine.length();
                }

                if (strLen != iTotalLineLen)
			        strLine = tool::readStreamLine(strStrm, strLen-iTotalLineLen, true, &bNeedMoreStr);

            }
            else
            {
                return 1;
            }
        } 
        while(strLen != iTotalLineLen);

		return 1;
	}

	int POP3::responseQUIT(stringstream *strStrm, int strLen)
	{
		bool bNeedMoreStr = false;
		string strLine = tool::readStreamLine(strStrm, strLen, false, &bNeedMoreStr);

		if (!bNeedMoreStr)
		{
			if(string::npos != strLine.find("+OK"))
			{
				return 0;
			}
			
			if(string::npos != strLine.find("-ERR"))
			{
				return -1;
			}
		}
		return 1;
	}
}